

#include "rtps/storages/PBufWrapper.h"
#include "rtps/utils/Log.h"
#include <stdarg.h>

#if PBUF_WRAP_VERBOSE && RTPS_GLOBAL_VERBOSE
#include "rtps/utils/printutils.h"
#define PBUF_WRAP_LOG(...)                                                     \
  if (true) {                                                                  \
    printf("[PBUF Wrapper] ");                                                 \
    printf(__VA_ARGS__);                                                       \
    printf("\n");                                                              \
  }
#else
#define PBUF_WRAP_LOG(...) //
#endif

static void
_pbw_copySimpleMembersAndResetBuffer(
    struct rtps_PBufWrapper *pbw,
    const struct rtps_PBufWrapper *other)
{
    pbw->m_freeSpace = other->m_freeSpace;

    if (pbw->firstElement != NULL) {
        pbuf_free(pbw->firstElement);
        pbw->firstElement = NULL;
    }
}

//constructor
struct rtps_PBufWrapper *
rtps_PBufWrapper_Construct(enum RTPS_PBufWrapperConstructType type, ...)
{
    struct rtps_PBufWrapper *pbw = (struct rtps_PBufWrapper *)calloc(1, sizeof(struct rtps_PBufWrapper));
    
    if (pbw == NULL)
        return NULL;
    
    pbw->m_freeSpace  = 0;
    pbw->m_layer      = PBUF_TRANSPORT;
    pbw->m_type       = PBUF_POOL;

    va_list ap;
    va_start(ap, type);

    if (type == RTPS_PBW_BY_PBUF) {
        struct pbuf *pbuf = va_arg(ap, struct pbuf *);

        if (pbuf == NULL) {
            free(pbw);
            return NULL;
        }

        pbw->firstElement = pbuf;
    } else {
        DataSize_t length = va_arg(ap, unsigned int);
        pbw->firstElement = pbuf_alloc(pbw->m_layer, length, pbw->m_type);
        pbw->m_freeSpace  = length;
    }
    va_end(ap);
    return pbw;
}

//destructor
void
rtps_PBufWrapper_Destruct( struct rtps_PBufWrapper *bufferWrapper)
{
    if (bufferWrapper && bufferWrapper->firstElement != NULL)
        pbuf_free(bufferWrapper->firstElement);
    free(bufferWrapper);
}

//operator =
void 
rtps_PBufWrapper_op_assign(
    struct rtps_PBufWrapper *dst, 
    struct rtps_PBufWrapper *other)
{
    _pbw_copySimpleMembersAndResetBuffer(dst, other);
    if (other->firstElement != NULL) {
        dst->firstElement = other->firstElement;
        other->firstElement = NULL;
    }
}

struct rtps_PBufWrapper *
rtps_PBufWrapper_init( struct pbuf *bufferToWrap )
{
    struct rtps_PBufWrapper *pbw =
        (struct rtps_PBufWrapper *) calloc(1, sizeof(struct rtps_PBufWrapper));

    if (pbw == NULL)
        return NULL;

    pbw->firstElement = bufferToWrap;
    pbw->m_freeSpace = 0;
    pbw->m_layer = PBUF_TRANSPORT;
    pbw->m_type = PBUF_POOL;
    return pbw;
}

void
rtps_PBufWrapper_deinit( struct rtps_PBufWrapper *bufferWrapper)
{
    if (bufferWrapper && bufferWrapper->firstElement != NULL)
        pbuf_free(bufferWrapper->firstElement);
    free(bufferWrapper);
}

bool
rtps_PBufWrapper_isValid(struct rtps_PBufWrapper *pbw)
{
    if (pbw->firstElement != NULL)
        return true;
    
    return false;
}

DataSize_t
rtps_PBufWrapper_spaceLeft(struct rtps_PBufWrapper *pbw)
{
    return pbw->m_freeSpace;
}

DataSize_t
rtps_PBufWrapper_spaceUsed(struct rtps_PBufWrapper *pbw)
{
    if (pbw->firstElement == NULL)
        return 0;

    return pbw->firstElement->tot_len - pbw->m_freeSpace;
}

bool
rtps_PBufWrapper_append(struct rtps_PBufWrapper *pbw, const uint8_t *data, DataSize_t length)
{
    DataSize_t spaceUsed;

    if (data == NULL || pbw == NULL) {
        return false;
    }

    spaceUsed = rtps_PBufWrapper_spaceUsed(pbw);
   
    err_t err = pbuf_take_at(pbw->firstElement, data, length, spaceUsed);
    if (err != ERR_OK) {
        return false;
    }
    //目前pbuf_take_at数据写不进去，临时单独写一遍。后续定位根因
    memcpy(((uint8_t *)pbw->firstElement->payload+spaceUsed), data, length);
    pbw->m_freeSpace -= length;
    return true;
}

void
rtps_PBufWrapper_append_cat(struct rtps_PBufWrapper *dstPbw, struct rtps_PBufWrapper *srcPbw)
{
    if (dstPbw == srcPbw)
        return;

    if (dstPbw->firstElement == NULL) {
        dstPbw = srcPbw;
        return;
    }

    dstPbw->m_freeSpace = srcPbw->m_freeSpace;

    pbuf_cat(dstPbw->firstElement, srcPbw->firstElement);

    srcPbw->firstElement = NULL;
}

bool
rtps_PBufWrapper_reserve(struct rtps_PBufWrapper *pbw, DataSize_t length)
{
    if (length <= pbw->m_freeSpace)
        return true;

    return rtps_PBufWrapper_increaseSizeBy(pbw, length - pbw->m_freeSpace);
}

void
rtps_PBufWrapper_reset(struct rtps_PBufWrapper *pbw)
{
    if (pbw->firstElement != NULL)
        pbw->m_freeSpace = pbw->firstElement->tot_len;
}

bool
rtps_PBufWrapper_increaseSizeBy(struct rtps_PBufWrapper *pbw, uint16_t length)
{
    struct pbuf *allocation = pbuf_alloc(pbw->m_layer, length, pbw->m_type);
    if (allocation == NULL)
        return false;

    pbw->m_freeSpace += length;

    if (pbw->firstElement == NULL) {
        pbw->firstElement = allocation;
    } else {
        pbuf_cat(pbw->firstElement, allocation);
    }

    return true;
}



